package com.sp.system.user.interceptor.security;

import com.sp.system.user.core.security.JWTAuthenticationFilter;
import com.sp.system.user.core.security.LoginAuthenticationFilter;
import com.sp.system.user.core.security.handler.MyAccessDeniedHandler;
import com.sp.system.user.core.security.handler.MyAuthenticationEntryPointHandler;
import com.sp.system.user.core.security.handler.MyAuthenticationFailHandler;
import com.sp.system.user.core.security.handler.MyAuthenticationSuccessHandler;
import com.sp.system.user.core.utils.security.JwtTokenUtil;
import com.sp.system.user.customer.service.impl.UserDetailsServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

/**
 * @Description:
 * web 安全性配置
 * 当用户登录时会进入此类的loadUserByUsername方法对用户进行验证，验证成功后会被保存在当前回话的principal对象中
 * 系统获取当前登录对象信息方法 WebUserDetails webUserDetails = (WebUserDetails)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
 * @Author: chenanhai
 * @Date: 2019年9月3日
 */
@Slf4j
@Configuration
//启动web安全性
@EnableWebSecurity
//开启方法级的权限注解,性设置后控制器层的方法前的@PreAuthorize("hasRole('admin')") 注解才能起效
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MyWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsServiceImpl myUserDetailService;
    @Autowired
    private MyAuthenticationEntryPointHandler myAuthEntryPointHandler;
    @Autowired
    private MyAccessDeniedHandler myAccessDeniedHandler;
    @Autowired
    private MyAuthenticationSuccessHandler myLoginSuccessHandler;
    @Autowired
    private MyAuthenticationFailHandler myLoginFailHandler;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//        super.configure(auth);
        // 配置指定用户权限信息  通常生产环境都是从数据库中读取用户权限信息而不是在这里配置
        //auth.inMemoryAuthentication().withUser("username1").password("123456").roles("USER").and().withUser("username2").password("123456").roles("USER","AMDIN");
        //基于数据库中的用户权限信息进行认证
        //指定密码加密所使用的加密器为bCryptPasswordEncoder()
        //需要将密码加密后写入数据库
        // myUserDetailService 类中获取了用户的用户名、密码以及是否启用的信息，查询用户所授予的权限，用来进行鉴权，查询用户作为群组成员所授予的权限
//        myUserDetailService.loadUserByUsername("user");
        auth.userDetailsService(myUserDetailService).passwordEncoder(new BCryptPasswordEncoder());
        //不删除凭据，以便记住用户
        //auth.eraseCredentials(false);
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        super.configure(web);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
//        super.configure(http);
        // 关闭跨站请求防护
        http.csrf().disable()
            //基于token，所以不需要session;如果基于session 则表使用这段代码
//            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
//            .and()
            //对请求进行认证
            //url认证配置顺序为：1.先配置放行不需要认证的 permitAll() 2.然后配置 需要特定权限的 hasRole() 3.最后配置 anyRequest().authenticated()
            .authorizeRequests()
            .antMatchers(JwtTokenUtil.antMatchers.split(",")).permitAll()
            //其他请求都需要进行认证,认证通过够才能访问
            // 待考证：如果使用重定向 httpServletRequest.getRequestDispatcher(url).forward(httpServletRequest,httpServletResponse); 重定向跳转的url不会被拦截（即在这里配置了重定向的url需要特定权限认证不起效），但是如果在Controller 方法上配置了方法级的权限则会进行拦截

            .anyRequest().authenticated()
        .and()
            .exceptionHandling()
            //在访问一个受保护的资源，用户没有通过登录认证，则抛出登录认证异常，MyAuthenticationEntryPointHandler类中commence()就会调用
//            .authenticationEntryPoint(myAuthEntryPointHandler)
            //在访问一个受保护的资源，用户通过了登录认证，但是权限不够，抛出授权异常，在myAccessDeniedHandler中处理
            .accessDeniedHandler(myAccessDeniedHandler)
        .and()
            .formLogin()
            //登录页面路径
//            .loginPage("/loginpage")
            //登录url
            .loginProcessingUrl("/login")//此登录url 和Controller 无关系
            //登录成功跳转路径
            .successForwardUrl("/api/public")
            //登录失败跳转路径
            .failureUrl("/")
            .permitAll()
            //登录成功后 MyAuthenticationSuccessHandler类中onAuthenticationSuccess（）被调用
            .successHandler(myLoginSuccessHandler)
            //登录失败后MyAuthenticationFailureHandler 类中onAuthenticationFailure（）被调用
            .failureHandler(myLoginFailHandler)
        .and()
            .logout().logoutUrl("/user/logout");

        //添加JWT过滤器 除/login其它请求都需经过此过滤器
        http.addFilter(new JWTAuthenticationFilter(authenticationManager()));
        http.addFilter(new LoginAuthenticationFilter(authenticationManager()));
    }
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
//    @Bean
//    CorsConfigurationSource corsConfigurationSource() {
//        final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
//        source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues());
//        return source;
//    }
}
